import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;

import util.StringTransformer;

public class Router {

	static final String ROUTER_FILE = "../roteador.config";
	static final String ENLACES_FILE = "../enlaces.config";

	static final int ID = 0;
	static final int PORT = 1;
	static final int ADDRESS = 2;
	static final int ROUTER1 = 0;
	static final int ROUTER2 = 1;
	static final int COST = 2;

	public static class ConfigRouter {
		int id;
		String port;
		String address;

		public ConfigRouter(int id, String port, String address) {
			this.id = id;
			this.port = port;
			this.address = address;
		}

		public int getId() {
			return this.id;
		}

		public String getPort() {
			return this.port;
		}

		public String getAddress() {
			return this.address;
		}
	}

	private HashMap<Integer, ConfigRouter> allConfigRouters;
	private ArrayList<Neighbor> neighbors;
	private HashMap<Integer, Boolean> connected;
	private int id;
	private String port;
	private String address;
	private DatagramSocket clientSocket;
	private ArrayList<Enlace> allEnlaces;
	private TabelaRotas tabela;
	private int counterReceived;
	private boolean[] neighborsAlive = new boolean[101];
	
	public boolean isConnected(int id) {
		return connected.get(id);
	}

	public void setConnected(int id) {
		connected.put(id, true);
	}

	public void setDisconnected(int id) {
		connected.put(id, false);
	}

	public Router(int id) {
		this.id = id;
		allConfigRouters = new HashMap<Integer, ConfigRouter>();
		neighbors = new ArrayList<Neighbor>();
		allEnlaces = new ArrayList<Enlace>();
		tabela = new TabelaRotas(this);
		connected = new HashMap<Integer, Boolean>();

		for (int i = 0; i < neighborsAlive.length; i++) {
			neighborsAlive[i] = false;
		}
	}

	public void setPort(String port) {
		this.port = port;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Integer getId() {
		return this.id;
	}

	public int getNumberOfNeighBors() {
		return neighbors.size();
	}

	public void addNeighBor(int id, int cost) {
		this.neighbors.add(new Neighbor(id, cost, false));
	}

	public ArrayList<Neighbor> getNeighbors() {
		return neighbors;
	}

	public HashMap<Integer, ConfigRouter> getRouters() {
		return allConfigRouters;
	}

	public String getAddress() {
		return this.address;
	}

	public String getPort() {
		return this.port;
	}

	public DatagramSocket getSocket() {
		return this.clientSocket;
	}

	public String getMessage(int idNb) {
		String ret = new String();
		VetorDistancia m = this.tabela.getVetorDistancia();

		ret += StringTransformer.intToString(this.id);

		for (int i = 1; i <= 100; i++) {
			ret += StringTransformer.intToString(i);
			
			if (m.containsKey(i)) {
				if (m.get(i).getId() != idNb && i != idNb) {
					ret += StringTransformer.intToString(m.get(i).getCusto());
				} else {
					ret += StringTransformer.intToString(TabelaRotas.INFINITY);
				}
			} else {
				ret += StringTransformer.intToString(TabelaRotas.INFINITY);
			}
		}

		return ret;
	}

	public void processMessage(String msg) {
		int idNo = Integer.parseInt(msg.substring(0, 3));
		
		VetorDistancia m = new VetorDistancia(idNo);
		for (int i = 3; i < msg.length(); i += 6) {
			int id = Integer.parseInt(msg.substring(i, i + 3));
			int custo = new Integer(msg.substring(i + 3, i + 6));
			m.put(id, new Custo(custo, idNo));
		}

		this.setConnected(idNo);

		m.setId(idNo);
		this.tabela.atualizarRotas(m);
	}

	public void setInativeNeighbor(Neighbor neighbor) {
		System.err.println("INATIVE>: " + neighbor.getId());
		if (!this.connected.get(neighbor.getId())) {
			tabela.atualizarRotas(neighbor.getId());
		}
	}

	public void setSocket(String address, String port) {
		try {
			this.clientSocket = new DatagramSocket(Integer.parseInt(port),
					InetAddress.getByName(address));
			clientSocket.setSoTimeout(300);
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		int idRouter = Integer.parseInt(args[0]);
		Router router = new Router(idRouter);
		Receiver receive = new Receiver(router);
		Sender send = new Sender(router);

		router.setId(idRouter);
		readConfigFile(router);
		readEnlaceFile(router);

		router.setAddress(router.allConfigRouters.get(idRouter).address);
		router.setPort(router.allConfigRouters.get(idRouter).port);
		router.setSocket(router.getAddress(), router.getPort());

		router.tabela.inicializar();
		new Thread(send).start();
		new Thread(receive).start();
	}

	private void setId(int parseInt) {
		this.id = parseInt;
	}

	private static void readEnlaceFile(Router router) {
		String s;
		String[] tokens;
		try {
			BufferedReader bf = new BufferedReader(new FileReader(new File(ENLACES_FILE)));
			while ((s = bf.readLine()) != null) {
				tokens = s.split(" ");
				
				int idR1 = Integer.parseInt(tokens[ROUTER1]);
				int idR2 = Integer.parseInt(tokens[ROUTER2]);
				int cost = Integer.parseInt(tokens[COST]);
				
				if (idR1 == router.getId()) {
					router.getNeighbors().get(router.getNeighbors().indexOf(new Neighbor(idR2, 0, false))).setCost(cost);
					router.getNeighbors().get(router.getNeighbors().indexOf(new Neighbor(idR2, 0, false))).setNeighbor(true);
				} else if (idR2 == router.getId()) {
					router.getNeighbors().get(router.getNeighbors().indexOf(new Neighbor(idR1, 0, false))).setCost(cost);
					router.getNeighbors().get(router.getNeighbors().indexOf(new Neighbor(idR1, 0, false))).setNeighbor(true);
				}

				router.getAllEnlaces().add(new Enlace(idR1, idR2, cost));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private ArrayList<Enlace> getAllEnlaces() {
		return this.allEnlaces;

	}

	// L� o arquivo roteador.config.
	private static void readConfigFile(Router router) {
		String s;
		String[] tokens;
		ConfigRouter cr;
		try {
			BufferedReader bf = new BufferedReader(new FileReader(new File(ROUTER_FILE)));
			while ((s = bf.readLine()) != null) {
				tokens = s.split(" ");

				int id = Integer.parseInt(tokens[ID]);
				String port = tokens[PORT];
				String address = tokens[ADDRESS];

				cr = new ConfigRouter(id, port, address);
				router.getRouters().put(id, cr);

				router.getNeighbors().add(new Neighbor(id, 999, false));
				router.setDisconnected(id);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void addCounterReceived() {
		this.counterReceived++;
	}

	public int getCounterReceived() {
		return counterReceived;
	}

	public void processNAlive(String msg) {
		neighborsAlive[new Integer(msg.substring(0, 3))] = true;
	}

	public void verificaNAlive() {
		if (getCounterReceived() >= getNumberOfNeighBors()) {
			for (Neighbor no : getNeighbors()) {

				if (no.isNeighbor()) {
					if (!neighborsAlive[no.getId()] && isConnected(no.getId())) {
						tabela.atualizarRotas(no.getId());
						connected.put(no.getId(), false);
					}
				}
				neighborsAlive[no.getId()] = false;
			}
			this.counterReceived = 0;
		}
	}


}
